package com.ke.forum.shiro.config.shiro;


import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.ke.forum.common.common.RedisKey;
import com.ke.forum.common.common.ResultCode;
import com.ke.forum.mbg.dao.SysRoleMapper;
import com.ke.forum.mbg.dao.UserMapper;
import com.ke.forum.mbg.entity.SysRole;
import com.ke.forum.mbg.entity.User;
import com.ke.forum.mbg.entity.dto.UserDto;
import com.ke.forum.mbg.mapstruct.UserMapstruct;
import com.ke.forum.shiro.exception.CustomException;
import com.ke.forum.shiro.utils.JWTUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;

import java.util.ArrayList;
import java.util.List;

@Slf4j
public class UserRealm extends AuthorizingRealm {
    // 授权
    @Autowired
    UserMapper userMapper;
    @Autowired
    SysRoleMapper roleMapper;
    @Autowired
    RedisTemplate<String,Object> redisTemplate;

    /**
     * 大坑！，必须重写此方法，不然Shiro会报错
     */
    @Override
    public boolean supports(AuthenticationToken token) {
        return token instanceof JWTToken;
    }

    //授权
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        System.out.println("进行授权");

        SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
        UserDto user = (UserDto) SecurityUtils.getSubject().getPrincipal();

        if (user.getPermissions() != null) {
            for (String per : user.getPermissions()) {
                authorizationInfo.addStringPermission(per);
            }
        }
        if (user.getRoles() != null) {
            user.getRoles().forEach(authorizationInfo::addRole);
        }
        return authorizationInfo;
    }

    //认证
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
        String userToken = (String) authenticationToken.getCredentials();
        //获取是否存在token
        UserDto o = (UserDto) redisTemplate.opsForValue().get(RedisKey.TOKEN_KEY + userToken);
        //存在则直接将对应的用户信息返回,跳过下面的验证
        if (o != null) {
            return new SimpleAuthenticationInfo(o, userToken, getName());
        }
        //如果token过期,抛出异常
        if (JWTUtils.isExpire(userToken)) {
            throw new AuthenticationException(new CustomException(ResultCode.PERMISSION_EXPIRE));
        }
        //从token中的payload拿出username,用户名为空抛出异常
        String email = JWTUtils.getEmail(userToken);
        if (email==null) {
            throw new AuthenticationException(new CustomException(ResultCode.PARAM_NOT_COMPLETE));
        }
        User userBean = userMapper.selectOne(Wrappers.<User>lambdaQuery().eq(User::getEmail,email));
        //如果用户不存在,抛出异常
        if (userBean == null) {
            throw new AuthenticationException(new CustomException(ResultCode.USER_NOT_EXIST));
        }
        //验证这个token是不是服务器签发的
        if (!JWTUtils.verify(userToken)) {
            throw new AuthenticationException(new CustomException(ResultCode.PERMISSION_TOKEN_INVALID));
        }

        //查询用户的权限
        UserDto userPermission = userMapper.getUserPermission(userBean.getId());
        UserDto userDto = UserMapstruct.INSTANCE.toDto(userBean);
        //查询用户的角色
        List<SysRole> roles = roleMapper.listUserRoles(userBean.getId());
        if (userPermission != null) {
            if (!userPermission.getPermissions().isEmpty()) {
                userDto.setPermissions(userPermission.getPermissions());
            }
        }
        List<String> r = new ArrayList<>();
        if (roles != null) {
            roles.forEach((SysRole u) -> r.add(u.getName()));
            userDto.setRoles(r);
        }
        //保存到当前线程,后面可以通过 UserDto user = (UserDto) SecurityUtils.getSubject().getPrincipal();获取
        return new SimpleAuthenticationInfo(userDto, userToken, getName());
    }
}
